/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
*/
#include "mem_allocater.h"

#include <linux/vmalloc.h>
#include "securec.h"

#include "dvpp_cmdlist_log.h"

void create_mem_pool(struct mem_pool_info** mem_pool, struct mutex* mtx)
{
    if (*mem_pool != NULL) {
        return;
    }
    mutex_lock(mtx); // mtx为全局锁，避免pool重复创建
    if (*mem_pool == NULL) {
        *mem_pool = (struct mem_pool_info*)vmalloc(sizeof(struct mem_pool_info));
        if (*mem_pool == NULL) {
            mutex_unlock(mtx);
            return;
        }
        (void)memset_s(*mem_pool, sizeof(struct mem_pool_info), 0, sizeof(struct mem_pool_info));
    }
    mutex_unlock(mtx);
}

int32_t init_mem_allocater(init func, struct mem_pool_info* mem_pool, uint32_t node_num,
    uint32_t node_size, struct mutex* mtx)
{
    int32_t ret = 0;
    if (mem_pool->is_inited == 1) {
        return ret;
    }
    mutex_lock(mtx); // mtx为全局锁，避免pool重复初始化

    if (mem_pool->is_inited == 0) {
        // 赋初值
        mem_pool->mem_node_num = node_num;
        mem_pool->node_size = node_size;
        mem_pool->base_nodes_addr = NULL;
        mem_pool->mem_size = mem_pool->mem_node_num * node_size;
        mem_pool->base_vaddr = NULL;

        // 分配地址和初始化链表
        ret = (*func)(mem_pool);
        if (ret == 0) {
            // 初始化jpegd pool中的锁和信号量，并更改初始化状态
            mutex_init(&mem_pool->mtx);
            sema_init(&mem_pool->sem, mem_pool->mem_node_num);
            mem_pool->is_inited = 1;
        }
    }

    mutex_unlock(mtx);
    return ret;
}

static void deinit_mem_allocater(deinit func, struct mem_pool_info* mem_pool)
{
    if (mem_pool->is_inited == 1) {
        // 销毁内存池子
        (*func)(mem_pool);
        mem_pool->is_inited = 0;
    }
}

void destroy_mem_pool(deinit func, struct mem_pool_info** mem_pool, struct mutex* mtx)
{
    if (*mem_pool == NULL) {
        return;
    }
    mutex_lock(mtx); // mtx为全局锁，避免pool重复销毁，pool使用时可以不用锁保护
                     // 由容器运行时不能删除已绑定的虚拟设备，因此pool使用时不会被销毁
    if (*mem_pool != NULL) {
        deinit_mem_allocater(func, *mem_pool);
        vfree(*mem_pool);
        *mem_pool = NULL;
    }
    mutex_unlock(mtx);
}

struct mem_node* alloc_node_from_pool(struct mem_pool_info* mem_pool)
{
    struct mem_node* pos;

    // 参数校验
    if (mem_pool == NULL) {
        DVPP_CMDLIST_LOG_ERROR("mem_pool is null\n");
        return NULL;
    }

    // 等待池子中有可用的node节点
    if (down_interruptible(&mem_pool->sem) != 0) {
        DVPP_CMDLIST_LOG_ERROR("waiting for mem_pool->sem fail, interrupt by signal\n");
        return NULL;
    }

    // 加锁
    mutex_lock(&mem_pool->mtx);

    // 从链表中获取一个node节点
    pos = NULL;
    list_for_each_entry(pos, &mem_pool->node_list.list, list) {
        if (pos->vaddr != NULL) {
            list_del_init(&pos->list);
            break;
        }
    }

    mutex_unlock(&mem_pool->mtx);
    DVPP_CMDLIST_LOG_DEBUG("alloc node success.\n");
    return pos;
}

void free_node_to_pool(struct mem_node* node, struct mem_pool_info* mem_pool)
{
    // 参数校验
    if ((node == NULL) || (node->vaddr == NULL) || (mem_pool == NULL)) {
        DVPP_CMDLIST_LOG_ERROR("node or vaddr or mem_pool is null\n");
        return;
    }

    // 归还节点node到池子pool
    mutex_lock(&mem_pool->mtx);
    list_add_tail(&node->list, &mem_pool->node_list.list);
    mutex_unlock(&mem_pool->mtx);
    DVPP_CMDLIST_LOG_DEBUG("free node success.\n");
    up(&mem_pool->sem);
}
